package com.lemeeting.conf.impl;

import java.util.ArrayList;
import java.util.HashMap;

import org.chromium.base.ObserverList;
import org.webrtc.videoengine.ViERenderer;

import android.hardware.Camera.CameraInfo;
import android.view.SurfaceView;
import android.view.ViewGroup;

import com.lemeeting.conf.IConferenceVideo;
import com.lemeeting.conf.IConferenceVideoObserver;
import com.lemeeting.conf.defines.QzPreviewInfo;
import com.lemeeting.conf.defines.QzVideoConfig;

public class QzConferenceVideo implements IConferenceVideo {
	long nativeConfVideo_ = 0;

	class PreviewerInfo {
		String account = null;
		int index_or_id_device = -1;
		int id_preview = -1;

		@Override
		public boolean equals(Object obj) {
			if (obj instanceof PreviewerInfo)
				return account.equals(((PreviewerInfo) obj).account)
						&& index_or_id_device == ((PreviewerInfo) obj).index_or_id_device
						&& id_preview == ((PreviewerInfo) obj).id_preview;
			else
				return false;
		}

		@Override
		public int hashCode() {
			return (int) (account.hashCode() ^ index_or_id_device ^ id_preview);
		}

		@Override
		public String toString() {
			return account + ":" + index_or_id_device + ":" + id_preview;
		}
	}

	class WindowsInfo {
		ViewGroup window;
		SurfaceView surfaceView;
		int refCount = 0;	
	}

	private final QzConferenceCenter center_;
	private final HashMap<PreviewerInfo, WindowsInfo> mapRemotePreviewInfo_ = new HashMap<PreviewerInfo, WindowsInfo>();
	private final HashMap<PreviewerInfo, WindowsInfo> mapLocalPreviewInfo_ = new HashMap<PreviewerInfo, WindowsInfo>();
	private final ArrayList<WindowsInfo>  list_window_info = new ArrayList<WindowsInfo>();  
	
    private final ObserverList<IConferenceVideoObserver> observer_list_ = new ObserverList<IConferenceVideoObserver>();

	QzConferenceVideo(QzConferenceCenter center) {
		center_ = center;
	}

	static public void setStartImage(String jpg_file) {
		jnisetStartImage(jpg_file);
	}

	static public void setTimeoutImage(String jpg_file) {
		jnisetTimeoutImage(jpg_file);
	}

	static public void setRemoteDisableRemoteVideoImage(String jpg_file) {
		jnisetRemoteDisableRemoteVideoImage(jpg_file);
	}

	static public void setLocalDisableRemoteVideoImage(String jpg_file) {
		jnisetLocalDisableRemoteVideoImage(jpg_file);
	}
	
	@Override
	public int addObserver(IConferenceVideoObserver observer) {
		if (observer_list_.hasObserver(observer))
			return -1;
		observer_list_.addObserver(observer);
		return 0;
	}

	@Override
	public int removeObserver(IConferenceVideoObserver observer) {
		observer_list_.removeObserver(observer);
		return 0;
	}
	
	@Override
	public SurfaceView createPreview() {
		SurfaceView surfaceView = ViERenderer.CreateRenderer(
				QzConferenceCenter.getContext(), true);
		return surfaceView;
	}

	WindowsInfo find(ViewGroup window) {
		for(int i = 0; i < this.list_window_info.size(); i++) {
			if(this.list_window_info.get(i).window == window) {
				return this.list_window_info.get(i);
			}
		}
		return null;
	}

	@Override
	public int addLocalPreview(int id_device, ViewGroup window, long context,
			int z_order, float left, float top, float right, float bottom) {
		
		WindowsInfo winfo = find(window);
		if(winfo == null) {
			SurfaceView surfaceView = ViERenderer.CreateRenderer(
					QzConferenceCenter.getContext(), true);
			
			winfo = new WindowsInfo();
			winfo.surfaceView = surfaceView;
			winfo.window = window;
			window.addView(winfo.surfaceView);
			list_window_info.add(winfo);
		}
		winfo.refCount++;

		QzPreviewInfo info = new QzPreviewInfo();
		info.account = "";
		info.index_or_id_device = id_device;
		info.window = winfo.surfaceView;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		info.context = context;
		
		int id_preview = jniaddLocalPreview(info);

		if (id_preview != -1) {
			PreviewerInfo pinfo = new PreviewerInfo();
			pinfo.account = "";
			pinfo.index_or_id_device = id_device;
			pinfo.id_preview = id_preview;
			mapLocalPreviewInfo_.put(pinfo, winfo);
			return id_preview;

		} else {
			winfo.refCount--;
			if(winfo.refCount == 0) {
				winfo.window.removeView(winfo.surfaceView);
				list_window_info.remove(winfo);
			}
			return -1;
		}
	}

	@Override
	public int removeLocalPreview(int id_device, int id_preview) {
		PreviewerInfo pinfo = new PreviewerInfo();
		pinfo.account = "";
		pinfo.index_or_id_device = id_device;
		pinfo.id_preview = id_preview;

		WindowsInfo winfo = mapLocalPreviewInfo_.get(pinfo);
		if (winfo == null)
			return -1;

		jniremoveLocalPreview(id_device, id_preview);
		mapLocalPreviewInfo_.remove(pinfo);

		winfo.refCount--;
		if(winfo.refCount == 0) {
			winfo.window.removeView(winfo.surfaceView);
			list_window_info.remove(winfo);
		}
		return 0;
	}

	@Override
	public int configureLocalPreview(int id_device, int id_preview,
			int z_order, float left, float top,	float right, float bottom) {
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = "";
		info.index_or_id_device = id_device;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		return jniconfigureLocalPreview(id_preview, info);		
	}

	@Override
	public int changeLocalPreviewWindow(int id_device, int id_preview, ViewGroup window,
			int z_order, float left, float top, float right, float bottom) {
		PreviewerInfo pinfo = new PreviewerInfo();
		pinfo.account = "";
		pinfo.index_or_id_device = id_device;
		pinfo.id_preview = id_preview;

		WindowsInfo winfo = mapLocalPreviewInfo_.get(pinfo);
		if (winfo == null)
			return -1;
		
		winfo.refCount--;
		if(winfo.refCount == 0) {
			winfo.refCount = 1;
			winfo.window.removeView(winfo.surfaceView);
			winfo.window = window;
			winfo.window.addView(winfo.surfaceView);
			
			QzPreviewInfo info = new QzPreviewInfo();
			info.account = "";
			info.index_or_id_device = id_device;
			info.z_order = z_order;
			info.left = left;
			info.top = top;
			info.right = right;
			info.bottom = bottom;
			return this.jniconfigureLocalPreview(id_preview, info);
		}
		else {
			winfo = find(window);
			if(winfo == null) {
				SurfaceView surfaceView = ViERenderer.CreateRenderer(
						QzConferenceCenter.getContext(), true);
				
				winfo = new WindowsInfo();
				winfo.surfaceView = surfaceView;
				winfo.window = window;
				window.addView(winfo.surfaceView);
				list_window_info.add(winfo);
			}
			winfo.refCount++;
			
			QzPreviewInfo info = new QzPreviewInfo();
			info.account = "";
			info.window = winfo.surfaceView;
			info.index_or_id_device = id_device;
			info.z_order = z_order;
			info.left = left;
			info.top = top;
			info.right = right;
			info.bottom = bottom;
			int ret = this.jniChangeLocalPreviewWindow(id_preview, info);
			if(ret == 0) {
				mapLocalPreviewInfo_.put(pinfo, winfo);
				return 0;
			}
			else {
				winfo.refCount--;
				if(winfo.refCount == 0) {
					winfo.window.removeView(winfo.surfaceView);
					list_window_info.remove(winfo);
				}
				return -1;
			}
		}
	}
	
	@Override
	public int addLocalPreview2(int id_device, SurfaceView window, long context,
			int z_order, float left, float top, float right, float bottom) {
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = "";
		info.index_or_id_device = id_device;
		info.window = window;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		info.context = context;
		
		return jniaddLocalPreview(info);
	}

	@Override
	public int removeLocalPreview2(int id_device, int id_preview) {
		return jniremoveLocalPreview(id_device, id_preview);
	}

	@Override
	public int configureLocalPreview2(int id_device, int id_preview,
			int z_order, float left, float top,	float right, float bottom) {
		return configureLocalPreview(id_device, id_preview,
				z_order, left, top, right, bottom);
	}

	@Override
	public int changeLocalPreviewWindow2(int id_device,
			int id_preview, SurfaceView window,
			int z_order, float left, float top, float right, float bottom) {
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = "";
		info.index_or_id_device = id_device;
		info.window = window;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		return jniChangeLocalPreviewWindow(id_preview, info);
	}

	@Override
	public int enableMirrorLocalPreview(int id_device, int id_preview,
			boolean enable, boolean mirror_xaxis, boolean mirror_yaxis) {
		return this.jnienableMirrorLocalPreview(id_device, id_preview, enable, mirror_xaxis, mirror_yaxis);
	}

	@Override
	public int mirrorLocalPreviewState(int id_device, int id_preview) {
		return this.jnimirrorLocalPreviewState(id_device, id_preview);
	}

	@Override
	public int rotateLocalPreview(int id_device, int id_preview, int rotation) {
		return this.jnirotateLocalPreview(id_device, id_preview, rotation);
	}

	@Override
	public int rotateLocalPreviewState(int id_device, int id_preview) {
		return this.jnirotateLocalPreviewState(id_device, id_preview);
	}

	@Override
	public int addRemotePreview(String account, int id_device, ViewGroup window,
			int z_order, float left, float top, float right, float bottom) {
		WindowsInfo winfo = find(window);
		if(winfo == null) {
			SurfaceView surfaceView = ViERenderer.CreateRenderer(
					QzConferenceCenter.getContext(), true);
			
			winfo = new WindowsInfo();
			winfo.surfaceView = surfaceView;
			winfo.window = window;
			window.addView(winfo.surfaceView);
			list_window_info.add(winfo);
		}
		winfo.refCount++;
		
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = account;
		info.index_or_id_device = id_device;
		info.window = winfo.surfaceView;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		
		int id_preview = jniaddRemotePreview(info);

		if (id_preview != -1) {
			PreviewerInfo pinfo = new PreviewerInfo();
			pinfo.account = account;
			pinfo.index_or_id_device = id_device;
			pinfo.id_preview = id_preview;

			mapRemotePreviewInfo_.put(pinfo, winfo);
			return id_preview;

		} else {
			winfo.refCount--;
			if(winfo.refCount == 0) {
				winfo.window.removeView(winfo.surfaceView);
				list_window_info.remove(winfo);
			}
			return -1;
		}
	}

	@Override
	public int removeRemotePreview(String account, int id_device,
			int id_preview) {
		PreviewerInfo pinfo = new PreviewerInfo();
		pinfo.account = account;
		pinfo.index_or_id_device = id_device;
		pinfo.id_preview = id_preview;

		WindowsInfo winfo = mapRemotePreviewInfo_.get(pinfo);
		if (winfo == null)
			return -1;

		jniremoveRemotePreview(account, id_device, id_preview);
		mapRemotePreviewInfo_.remove(pinfo);
		
		winfo.refCount--;
		if(winfo.refCount == 0) {
			winfo.window.removeView(winfo.surfaceView);
			list_window_info.remove(winfo);
		}

		return 0;
	}

	@Override
	public int configureRemotePreview(String account, int id_device, int id_preview,
			int z_order, float left, float top,	float right, float bottom) {
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = account;
		info.index_or_id_device = id_device;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		return this.jniconfigureRemotePreview(id_preview, info);	
		
	}

	@Override
	public int changeRemotePreviewWindow(String account, int id_device,
			int id_preview, ViewGroup window,
			int z_order, float left, float top,	float right, float bottom) {
		PreviewerInfo pinfo = new PreviewerInfo();
		pinfo.account = account;
		pinfo.index_or_id_device = id_device;
		pinfo.id_preview = id_preview;

		WindowsInfo winfo = mapRemotePreviewInfo_.get(pinfo);
		if (winfo == null)
			return -1;
		
		winfo.refCount--;
		if(winfo.refCount == 0) {
			winfo.refCount = 1;
			winfo.window.removeView(winfo.surfaceView);
			winfo.window = window;
			winfo.window.addView(winfo.surfaceView);
			
			QzPreviewInfo info = new QzPreviewInfo();
			info.account = account;
			info.index_or_id_device = id_device;
			info.z_order = z_order;
			info.left = left;
			info.top = top;
			info.right = right;
			info.bottom = bottom;
			return this.jniconfigureRemotePreview(id_preview, info);
		}
		else {
			winfo = find(window);
			if(winfo == null) {
				SurfaceView surfaceView = ViERenderer.CreateRenderer(
						QzConferenceCenter.getContext(), true);
				
				winfo = new WindowsInfo();
				winfo.surfaceView = surfaceView;
				winfo.window = window;
				window.addView(winfo.surfaceView);
				list_window_info.add(winfo);
			}
			winfo.refCount++;
			
			QzPreviewInfo info = new QzPreviewInfo();
			info.account = account;
			info.window = winfo.surfaceView;
			info.index_or_id_device = id_device;
			info.z_order = z_order;
			info.left = left;
			info.top = top;
			info.right = right;
			info.bottom = bottom;
			int ret = this.jniconfigureRemotePreview(id_preview, info);
			if(ret == 0) {
				mapRemotePreviewInfo_.put(pinfo, winfo);
				return 0;
			}
			else {
				winfo.refCount--;
				if(winfo.refCount == 0) {
					winfo.window.removeView(winfo.surfaceView);
					list_window_info.remove(winfo);
				}
				return -1;
			}
		}
	}

	@Override
	public int addRemotePreview2(String account, int id_device,
			SurfaceView window, int z_order, float left, float top, float right,
			float bottom) {
	
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = account;
		info.index_or_id_device = id_device;
		info.window = window;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		
		return jniaddRemotePreview(info);
	}

	@Override
	public int removeRemotePreview2(String account, int id_device,
			int id_preview) {
		return jniremoveRemotePreview(account, id_device, id_preview);
	}

	@Override
	public int configureRemotePreview2(String account, int id_device, int id_preview,
			int z_order, float left, float top,	float right, float bottom) {
		return configureRemotePreview(account, id_device, id_preview, 
				z_order, left, top, right, bottom);
	}

	@Override
	public int changeRemotePreviewWindow2(String account, int id_device,
			int id_preview, SurfaceView window,
			int z_order, float left, float top, float right, float bottom) {
		QzPreviewInfo info = new QzPreviewInfo();
		info.account = account;
		info.index_or_id_device = id_device;
		info.window = window;
		info.z_order = z_order;
		info.left = left;
		info.top = top;
		info.right = right;
		info.bottom = bottom;
		return this.jnichangeRemotePreviewWindow(id_preview, info);
	}

	@Override
	public int enableMirrorRemotePreview(String account, int id_device, int id_preview,
			boolean enable, boolean mirror_xaxis, boolean mirror_yaxis) {
		return this.jnienableMirrorRemotePreview(account, id_device, id_preview, enable, mirror_xaxis, mirror_yaxis);
	}

	@Override
	public int mirrorRemotePreviewState(String account, int id_device, int id_preview) {
		return this.jnimirrorRemotePreviewState(account, id_device, id_preview);
	}

	@Override
	public int rotateRemotePreview(String account, int id_device, int id_preview, int rotation) {
		return this.jnirotateRemotePreview(account, id_device, id_preview, rotation);
	}

	@Override
	public int rotateRemotePreviewState(String account, int id_device, int id_preview) {
		return this.jnirotateRemotePreviewState(account, id_device, id_preview);
	}

	@Override
	public int enableRemotePreviewColorEnhancement(String account, int id_device, boolean enable) {
		return this.jnienableRemotePreviewColorEnhancement(account, id_device, enable);	
	}
		
	@Override
	public boolean remotePreviewColorEnhancementState(String account, int id_device) {
		return this.jniremotePreviewColorEnhancementState(account, id_device);			
	}
	
	@Override
	public int enableRemotePreview(String account, int id_device,
			boolean enable) {
		return jnienableRemotePreview(account, id_device, enable);
	}

	@Override
	public boolean getRemotePreviewState(String account, int id_device) {
		return jnigetRemotePreviewState(account, id_device);
	}

	@Override
	public int numOfDevice() {
		return jninumOfDevice();
	}

	@Override
	public String getDeviceName(int id_device) {
		return jnigetDeviceName(id_device);
	}

	@Override
	public int[] getDeviceFormat(int id_device) {
		return jnigetDeviceFormat(id_device);
	}

	@Override
	public int isFrontCamera(int indexdevice) {
		return indexdevice == CameraInfo.CAMERA_FACING_FRONT ? 1 : 0;
	}

	@Override
	public int switchMobiledevicesCamera() {
		return jniswitchMobiledevicesCamera();
	}

	@Override
	public int getMobiledevicesActiveCameraId() {
		return jnigetMobiledevicesActiveCameraId();
	}

	@Override
	public boolean isCaptureing(int id_device) {
		return jniisCaptureing(id_device);
	}

	@Override
	public int setVideoConfig(int id_device, QzVideoConfig config) {
		return jnisetVideoConfig(id_device, config);
	}

	@Override
	public QzVideoConfig getVideoConfig(int id_device) {
		return jnigetVideoConfig(id_device);
	}

	@Override
	public int enableDeflickering(int id_device, boolean enable) {
		return jnienableDeflickering(id_device, enable);		
	}
	
	@Override
	public boolean deflickeringState(int id_device) {
		return jnideflickeringState(id_device);		
	}

	@Override
	public int getCaptureDeviceSnapshot(int id_device, String file_name) {
		return jnigetCaptureDeviceSnapshot(id_device, file_name);
	}

	@Override
	public int getRenderSnapshot(String account, int id_device,
			int id_preview, String file_name_utf8) {
		return jnigetRenderSnapshot(account, id_device, id_preview,
				file_name_utf8);
	}

	@Override
	public int setShowNameInVideo(boolean enable) {
		return jnisetShowNameInVideo(enable);
	}

	@Override
	public boolean getShowNameInVideoState() {
		return jnigetShowNameInVideoState();
	}

	@Override
	public int enableDebugVideo(boolean enable) {
		return jnienableDebugVideo(enable);
	}

	@Override
	public boolean getDebugVideoState() {
		return jnigetDebugVideoState();
	}

	void onAddLocalPreview(int id_device, int id_preview, long context, int error) {
		for (IConferenceVideoObserver obeserver : observer_list_)
			obeserver.onAddLocalPreview(id_device, id_preview, context, error);
	}

	void onVideoDeviceChanged(int id_changed_dev, boolean is_add, int dev_nums, int id_main_dev) {
		for (IConferenceVideoObserver obeserver : observer_list_)
			obeserver.onVideoDeviceChanged(id_changed_dev, is_add, dev_nums, id_main_dev);
	}

	void onLocalVideoResolutionChanged(int id_device, int previewindex, int width, int height) {
		for (IConferenceVideoObserver obeserver : observer_list_)
			obeserver.onLocalVideoResolutionChanged(id_device, previewindex, width, height);
	}

	void onRemoteVideoResolutionChanged(String account, int id_device, int previewindex, int width, int height) {
		for (IConferenceVideoObserver obeserver : observer_list_)
			obeserver.onRemoteVideoResolutionChanged(account, id_device, previewindex, width, height);		
	}

	static private native void jnisetStartImage(String jpg_file);

	static private native void jnisetTimeoutImage(String jpg_file);

	static private native void jnisetRemoteDisableRemoteVideoImage(String jpg_file);

	static private native void jnisetLocalDisableRemoteVideoImage(String jpg_file);

	private native int jniaddLocalPreview(QzPreviewInfo info);

	private native int jniremoveLocalPreview(int indexdevice, int id_preview);
	
	private native int jniconfigureLocalPreview(int id_preview, QzPreviewInfo info);

	private native int jniChangeLocalPreviewWindow(int id_preview, QzPreviewInfo info);

	private native int jnienableMirrorLocalPreview(int indexdevice, int id_preview,
			boolean enable, boolean mirror_xaxis, boolean mirror_yaxis);

	private native int jnimirrorLocalPreviewState(int indexdevice, int id_preview); 

	private native int jnirotateLocalPreview(int indexdevice, int id_preview, int rotation);

	private native int jnirotateLocalPreviewState(int indexdevice, int id_preview);

	private native int jniaddRemotePreview(QzPreviewInfo info);

	private native int jniremoveRemotePreview(String account, int id_device,
			int id_preview);

	private native int jniconfigureRemotePreview(int id_preview, QzPreviewInfo info);
	
	private native int jnichangeRemotePreviewWindow(int id_preview, QzPreviewInfo info);
	
	private native int jnienableMirrorRemotePreview(String account, int id_device, int id_preview,
			boolean enable, boolean mirror_xaxis, boolean mirror_yaxis);

	private native int jnimirrorRemotePreviewState(String account, int id_device, int id_preview);

	private native int jnirotateRemotePreview(String account, int id_device, int id_preview, int rotation);

	private native int jnirotateRemotePreviewState(String account, int id_device, int id_preview);

	private native int jnienableRemotePreviewColorEnhancement(String account, int id_device, boolean enable);

	private native boolean jniremotePreviewColorEnhancementState(String account, int id_device);
	
	private native int jnienableRemotePreview(String account, int id_device,	boolean enable);

	private native boolean jnigetRemotePreviewState(String account,	int id_device);

	private native int jninumOfDevice();

	private native String jnigetDeviceName(int indexdevice);

	private native int[] jnigetDeviceFormat(int indexdevice);

	private native int jniswitchMobiledevicesCamera();

	private native int jnigetMobiledevicesActiveCameraId();
	
	private native boolean jniisCaptureing(int indexdevice);

	private native int jnisetVideoConfig(int indexdevice, QzVideoConfig config);

	private native QzVideoConfig jnigetVideoConfig(int indexdevice);

	private native int jnienableDeflickering(int indexdevice, boolean enable);
	
	private native boolean jnideflickeringState(int indexdevice);

	private native int jnigetCaptureDeviceSnapshot(int indexdevice,	String file_name);

	private native int jnigetRenderSnapshot(String account, int id_device,
			int id_preview, String file_name);

	private native int jnisetShowNameInVideo(boolean enable);

	private native boolean jnigetShowNameInVideoState();

	private native int jnienableDebugVideo(boolean enable);

	private native boolean jnigetDebugVideoState();

}
